package cn.kgm.executor;

import cn.kgm.pojo.BoundSql;
import cn.kgm.pojo.Configuration;
import cn.kgm.pojo.MappedStatement;
import cn.kgm.util.GenericTokenParser;
import cn.kgm.util.ParameterMapping;
import cn.kgm.util.ParameterMappingTokenHandler;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class SimpleExecutor implements Executor {

    private Connection connection = null;
    private PreparedStatement preparedStatement = null;
    private ResultSet resultSet = null;


    @Override
    public <E> List<E> query(Configuration configuration, MappedStatement mappedStatement, Object param) throws Exception {
        // 1,加载驱动,获取数据库连接
        connection = configuration.getDataSource().getConnection();
        // 2,获取preparedStatement预编译对象
        String sql = mappedStatement.getSql();
        BoundSql boundSql = getBoundSql(sql);
        String finalSql = boundSql.getFinalSql();
        // jdbc接受一个参数为?的sql语句
        preparedStatement = connection.prepareStatement(finalSql);
        parameterHandle(mappedStatement, boundSql, param);
        resultSet = preparedStatement.executeQuery();
        Class<?> resultClass = Class.forName(mappedStatement.getResultType());
        List<E> results = new ArrayList<E>();
        while (resultSet.next()) {
            // 获取此java对象的实体类
            Object o = resultClass.newInstance();
            // 拿到sql查询到的所有字段
            ResultSetMetaData metaData = resultSet.getMetaData();
            for (int index = 1; index <= metaData.getColumnCount(); index++) {
                // 获取字段名
                String columnName = metaData.getColumnName(index);
                // 获取字段值
                Object value = resultSet.getObject(columnName);
                // 使用字段名,和javabean的class对象获取该字段的属性描述器
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(columnName, resultClass);
                // 根据字段的属性描述器,获取他的set方法
                Method writeMethod = propertyDescriptor.getWriteMethod();
                // 根据属性描述器,获取字段的get方法
                // Method readMethod = propertyDescriptor.getReadMethod();
                // 根据拿到的方法,给属性设置值.这里需要两个参数[0,属性,1,属性所属对象的实体]
                writeMethod.invoke(o, value);
            }
            results.add((E) o);
        }
        return results;
    }

    private BoundSql getBoundSql(String sql) {
        // 1,创建标记处理器 ,配合 标记解析器完成标记 的 解析工作
        ParameterMappingTokenHandler parameterMappingTokenHandler = new ParameterMappingTokenHandler();
        // 2,创建 标记解析器
        GenericTokenParser genericTokenParser = new GenericTokenParser("#{", "}", parameterMappingTokenHandler);
        // #{}占位符换成? 2.解析替换过程中,将#{}里面的值保存下来 parameterMappings
        String finalSql = genericTokenParser.parse(sql);
        List<ParameterMapping> parameterMappings = parameterMappingTokenHandler.getParameterMappings();
        return new BoundSql(finalSql, parameterMappings);
    }

    private void parameterHandle(MappedStatement mappedStatement, BoundSql boundSql, Object param) throws Exception {
        if (null == param) return;
        Class<?> parameterClass = Class.forName(mappedStatement.getParameterType());
        int i = 1;
        for (ParameterMapping parameterMapping : boundSql.getParameterMappingList()) {
            // 根据javabean的字段名,获取该字段的信息
            Field declaredField = parameterClass.getDeclaredField(parameterMapping.getContent());
            // 让类中private的属性变得可访问
            declaredField.setAccessible(true);
            // 从param中拿到declaredField这个字段的值
            Object value = declaredField.get(param);
            // 设置值,替换 ? [preparedStatement]是jdbc的对象
            preparedStatement.setObject(i, value);
            i++;
        }
    }

    @Override
    public void close() throws SQLException {
        if (connection != null) {
            connection.close();
        }
        if (preparedStatement != null) {
            preparedStatement.close();
        }
        if (resultSet != null) {
            resultSet.close();
        }
    }
}
